package cn.lsg.util;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.zip.GZIPInputStream;

import org.apache.log4j.Logger;

public class ThreadDownload {

	public synchronized byte[] down(String urlStr) throws Exception {
		ByteArrayOutputStream bit = new ByteArrayOutputStream();
		ExecutorService pool = Executors.newFixedThreadPool(1);
		urlStr = urlStr.replaceAll("( *)$", "");
		urlStr = UtilEncrypt.URIEncode(urlStr);
		URL url = new URL(urlStr);
		Thread t = new Thread(new AreaDivide(10, bit, url, this));
		pool.execute(t);
		pool.shutdown();
		this.wait();
		return bit.toByteArray();

	}

	public static void main(String[] args) throws Exception {
		byte[] bit = new ThreadDownload().down("http://www.qq.com/");
		FileOutputStream fos = new FileOutputStream(
				"/usr/local/work/test/1.html");
		fos.write(bit);
		fos.flush();
		fos.close();
	}
}

class AreaDivide extends Thread {
	public static int t = 0;
	private static final Logger log = Logger.getLogger(AreaDivide.class);

	private int total = 0, threadCount = 3;
	private URL url;
	private ByteArrayOutputStream bit = null;
	private Object obj;

	public AreaDivide(int threadCount, ByteArrayOutputStream bit, URL url,
			Object obj) {
		this.threadCount = threadCount;
		this.url = url;
		this.bit = bit;
		this.obj = obj;
	}

	@Override
	public void run() {
		synchronized (this) {
			synchronized (obj) {
				URLConnection conn;
				try {
					conn = url.openConnection();
					conn.setRequestProperty("Host", url.getHost());
					conn.setRequestProperty("Accept-Language",
							"zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");
					conn.setRequestProperty("Referer", url.toExternalForm());
					conn.setRequestProperty("Connection", "keep-alive");
					conn.setRequestProperty(
							"User-Agent",
							"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:13.0) Gecko/20100101 Firefox/13.0");
					total = conn.getContentLength();
					if (total == -1) {// 未知大小，单线程
						threadCount = 1;
					}
					log.debug("文件大小：" + total);
					int h = total / threadCount;

					Map<Integer, Integer> state = new HashMap<Integer, Integer>();

					List<ByteArrayOutputStream> byteList = new ArrayList<ByteArrayOutputStream>();

					ExecutorService pool = Executors.newFixedThreadPool(3);
					for (int i = 0; i < threadCount; i++) {
						int b = i * h, e = i * h + h - 1;
						if (i + 1 >= threadCount) {
							e = total;
						}
						ByteArrayOutputStream _bit = new ByteArrayOutputStream();
						Thread t = new Thread(new DownArea(b, e,
								url.openConnection(), _bit, this, i, state));
						pool.execute(t);
						state.put(i, 0);
						byteList.add(_bit);
						log.debug("[" + i + "]线程：" + b + "-" + e);
					}
					pool.shutdown();
					wait();

					for (ByteArrayOutputStream os : byteList) {// 碎片拼合
						bit.write(os.toByteArray());
					}
					obj.notify();
				} catch (IOException e) {
					e.printStackTrace();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

		}
	}
}

class DownArea extends Thread {// 区块下载
	private static final Logger log = Logger.getLogger(ThreadDownload.class);
	private int b = 0, e = 0, threadId;
	private URLConnection conn;
	private ByteArrayOutputStream bit = null;
	private Object preObj;
	private Map<Integer, Integer> state;

	public DownArea(int b, int e, URLConnection conn,
			ByteArrayOutputStream bit, Object obj, int threadId,
			Map<Integer, Integer> state) {
		this.b = b;
		this.e = e;

		this.conn = conn;
		this.bit = bit;
		this.threadId = threadId;
		this.state = state;
		this.preObj = obj;

	}

	private final synchronized void go() throws IOException {
		InputStream is = null;
		try {
			conn.setRequestProperty("Range", "bytes=" + b + "-" + e + "");
			conn.connect();
			is = conn.getInputStream();
			if ("gzip".equals(conn.getContentEncoding())) {
				is = new GZIPInputStream(is);
			}
			int len = 0;
			byte[] _bit = new byte[1024];
			while ((len = is.read(_bit)) != -1) {
				bit.write(_bit, 0, len);
			}
			state.put(threadId, 1);
		} finally {
			is.close();
			bit.flush();
			bit.close();
		}
	}

	@Override
	public void run() {
		try {
			synchronized (this) {
				go();
				Iterator<Entry<Integer, Integer>> itr = state.entrySet()
						.iterator();
				boolean flag = true;
				while (itr.hasNext()) {
					Map.Entry<Integer, Integer> entry = itr.next();
					if (entry.getValue() != 1) {
						flag = false;
						break;
					}
				}
				if (flag) {
					synchronized (preObj) {
						preObj.notify();
					}
				}
			}
		} catch (IOException e) {
			try {
				go();
			} catch (IOException e1) {
				try {
					go();
				} catch (IOException e2) {
					log.error("[" + threadId + "]线程下载异常", e2);
				}
			}
		}
	}
}
